﻿using AutoMapper;
using Benefactor.Models.DtoModels;
using Benefactor.Services;
using Benefactor.Services.Repositories;
using Bigway.Common.Models.DtoModels;
using Bigway.Common.Services.Repositories;
using Bigway.Common.Utils;
using Microsoft.AspNetCore.Mvc;

namespace Benefactor.Controllers;
using static Errors;

[ApiController]
[Route("api/issued_benefits_card")]
public class IssuedBenefitsCardController(
    IIssuedBenefitsCardRepository issuedBenefitsCardRepository,
    ISoftwareSystemRepository softwareSystemRepository,
    ICrmebMerchantRepository crmebMerchantRepository,
    IMapper mapper,
    IAppConfig appConfig,
    ILogger<IssuedBenefitsCardController> logger): ControllerBase
{
    private string AttachCrmebImageBaseUrl(string relativeUrl)
    {
        return UrlUtils.IsAbsoluteUrl(relativeUrl)
            ? relativeUrl 
            : UrlUtils.GetAbsoluteUrl(appConfig.CrmebImageBaseUrl, relativeUrl);
    }

    [HttpGet]
    public async Task<GetIssuedBenefitsCardsResponseDto> GetIssuedBenefitsCards(
        [FromQuery(Name = "api_key")] string apiKey,
        [FromQuery] GetIssuedBenefitsCardsRequestDto request,
        CancellationToken token = default
    )
    {
        var systemId = request.system_id;
        var userId = request.user_id;
        var issuedBenefitsCardId = request.issued_benefits_card_id;
        var benefitsCardId = request.benefits_card_id;
        logger.LogInformation("Getting issued benefits cards for system {SystemId}",
            systemId);
        if (apiKey != appConfig.ApiKey)
            return Unauthorized<GetIssuedBenefitsCardsResponseDto>();

        var system = await softwareSystemRepository.GetAsync(systemId, token);
        if (system is null)
            return SystemNotFound<GetIssuedBenefitsCardsResponseDto>(systemId);

        var includeVouchers = request.include_vouchers?? false;

        var issuedBenefitsCards = await issuedBenefitsCardRepository.GetAllAsync(
            query =>
            {
                query = query.Where(x => !x.IsDeleted && x.SystemId == systemId);
                if (!string.IsNullOrEmpty(userId))
                    query = query.Where(x => x.UserId == userId);
                if (!string.IsNullOrEmpty(issuedBenefitsCardId))
                    query = query.Where(x => x.Id == issuedBenefitsCardId);
                if (!string.IsNullOrEmpty(benefitsCardId))
                    query = query.Where(x => x.BenefitsCardId == benefitsCardId);

                if (includeVouchers)
                {
                    query = query.Includes(x => x.Vouchers, v => v.Benefit)
                        .Includes(x => x.Vouchers, v => v.Product);
                }

#pragma warning disable CS8604 // Possible null reference argument.
                query = query.Where(x => x.Vouchers.Any());
#pragma warning restore CS8604 // Possible null reference argument.

                query = query.OrderByDescending(x => x.IssueTime);

                return query.Includes(x => x.BenefitsCard);
            }, token);

        if (issuedBenefitsCards.Count == 0)
            return new GetIssuedBenefitsCardsResponseDto();

        if (!includeVouchers)
        {
            return new GetIssuedBenefitsCardsResponseDto
            {
                Data = issuedBenefitsCards.Select(mapper.Map<IssuedBenefitsCardInfo>)
            };
        }

        var allVouchers = issuedBenefitsCards
                .SelectMany(x => x.Vouchers!);

        var crmebMerchantIds = allVouchers.Select(x => x.Product?.MerchantId)
            .Where(x => x != null)
            .Distinct()
            .Cast<string>()
            .Select(int.Parse)
            .ToList();

        var crmebMerchants = await crmebMerchantRepository.GetAllAsync(
            query =>
                query.Where(x => x.IsDeleted == 0 && crmebMerchantIds.Contains(x.Id)),
            token);

        var simpleMerchantInfosMap = crmebMerchants.Select(merchant =>
        {
            var merchantAddress = merchant.Province +
                                  merchant.City +
                                  merchant.District +
                                  merchant.AddressDetail;

            var merchantInfo = new SimpleMerchantInfoWithBenefits
            {
                Id = merchant.Id.ToString(),
                Phone = merchant.Phone,
                Name = merchant.Name,
                Introduction = merchant.Introduction,
                Address = merchantAddress,
                Latitude = double.TryParse(merchant.Latitude, out var latitude)? latitude : null,
                Longitude = double.TryParse(merchant.Longitude, out var longitude) ? longitude : null,
                Images = new MerchantImageInfo
                {
                    Cover = AttachCrmebImageBaseUrl(merchant.CoverImage)
                }
            };

            return merchantInfo;
        }).ToDictionary(x => x.Id);

        var issuedBenefitsCardInfos = issuedBenefitsCards
            .Select(x =>
            {
                var issuedBenefitsCardInfo = mapper.Map<IssuedBenefitsCardInfo>(x);
                var vouchers = x.Vouchers!;
                issuedBenefitsCardInfo.VoucherValidFrom = vouchers[0].ValidFrom;
                issuedBenefitsCardInfo.VoucherValidTo = vouchers[0].ValidTo;
                var vouchersGroupedByBenefitId = vouchers
                    .GroupBy(v => v.BenefitId);
                var voucherGroupsGroupedByMerchantIds = vouchersGroupedByBenefitId
                    .GroupBy(g => g.First().MerchantId);

                var merchantInfos = voucherGroupsGroupedByMerchantIds
                    .Select(g =>
                    {
                        var merchantInfo = simpleMerchantInfosMap[g.Key];
                        // Make a clone so that the original won't be overriden
                        merchantInfo = merchantInfo.Clone();

                        var benefitInfos = g.Select(
                            voucherGroup =>
                            {
                                var firstVoucher = voucherGroup.First();
                                var benefit = firstVoucher.Benefit!;
                                benefit.Product = firstVoucher.Product;

                                var benefitInfo = mapper.Map<BenefitInfoWithVouchers>(benefit);
                                benefitInfo.CreateTime = null;
                                benefitInfo.UpdateTime = null;
                                benefitInfo.Product.CreateTime = null;
                                benefitInfo.Product.UpdateTime = null;
                                var voucherInfos = voucherGroup.Select(voucher =>
                                {
                                    var voucherInfo = mapper.Map<VoucherInfo>(voucher);
                                    voucherInfo.CreateTime = voucherInfo.UpdateTime = null;
                                    voucherInfo.Name =
                                        voucherInfo.MerchantId =
                                            voucherInfo.IssuedBenefitsCardNo =
                                                voucherInfo.UserId =
                                                    voucherInfo.BenefitId =
                                                        voucherInfo.ProductId =
                                                            null;
                                    voucherInfo.ValidFrom = null;
                                    voucherInfo.ValidTo = null;

                                    return voucherInfo;
                                });
                                
                                benefitInfo.Vouchers = voucherInfos;
                                return benefitInfo;
                            }
                        );

                        merchantInfo.Benefits = benefitInfos;

                        return merchantInfo;
                    });
                issuedBenefitsCardInfo.Merchants = merchantInfos;

                return issuedBenefitsCardInfo;
            });

        return new GetIssuedBenefitsCardsResponseDto
        {
            Data = issuedBenefitsCardInfos
        };
    }
}